﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace RMC.Commands
{
	internal class Win32Helpers
	{
		public const int INPUT_KEYBOARD = 1;
		public const int KEYEVENTF_KEYUP = 2;

		[StructLayout(LayoutKind.Sequential)]
		public struct INPUT
		{
			public int type;
			public INPUTUNION inputUnion;
		}

		[StructLayout(LayoutKind.Explicit)]
		public struct INPUTUNION
		{
			// Fields
			[FieldOffset(0)]
			public HARDWAREINPUT hi;
			[FieldOffset(0)]
			public KEYBDINPUT ki;
			[FieldOffset(0)]
			public MOUSEINPUT mi;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct HARDWAREINPUT
		{
			public int uMsg;
			public short wParamL;
			public short wParamH;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct KEYBDINPUT
		{
			public ushort wVk;
			public short wScan;
			public int dwFlags;
			public int time;
			public IntPtr dwExtraInfo;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct MOUSEINPUT
		{
			public int dx;
			public int dy;
			public int mouseData;
			public int dwFlags;
			public int time;
			public IntPtr dwExtraInfo;
		}

		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
		public static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

		[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
		public static extern bool SetForegroundWindow(IntPtr hWnd);

		public static IntPtr GetWindowHandle()
		{
			IntPtr hwnd = IntPtr.Zero;
			int mySession;

			using (Process currentProcess = Process.GetCurrentProcess())
				mySession = currentProcess.SessionId;

			foreach (Process p in Process.GetProcessesByName("ehshell"))
			{
				if (p.SessionId == mySession)
					hwnd = p.MainWindowHandle;
				p.Dispose();
			}
			return hwnd;
		}

		public static CommandResult SendKeyCmd(ushort vk)
		{
			return SendKeyCmd(vk, false, false, false, false);
		}

		public static CommandResult SendKeyCmd(ushort vk, bool shift, bool ctrl, bool alt)
		{
			return SendKeyCmd(vk, shift, ctrl, alt, false);
		}

		public static CommandResult SendKeyCmd(ushort vk, bool shift, bool ctrl, bool alt, bool win)
		{
			CommandResult cr = new CommandResult();
			cr.Type = CommandResultType.Executed;

			INPUT structInput;
			structInput = new INPUT();
			structInput.type = INPUT_KEYBOARD;
			structInput.inputUnion.ki.wScan = 0;
			structInput.inputUnion.ki.time = 0;
			structInput.inputUnion.ki.dwFlags = 0;
			structInput.inputUnion.ki.dwExtraInfo = IntPtr.Zero;
			try
			{
				if (SetForegroundWindow(GetWindowHandle()) == false)
				{
					cr.Type = CommandResultType.Exception;
					cr.Content = "SetForegroundWindow return false!";
					return cr;
				}

				if (shift)
				{
					structInput.inputUnion.ki.wVk = 0x10;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				if (ctrl)
				{
					structInput.inputUnion.ki.wVk = 0x11;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				if (alt)
				{
					structInput.inputUnion.ki.wVk = 0x12;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				if (win)
				{
					structInput.inputUnion.ki.wVk = 0x5b;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				structInput.inputUnion.ki.wVk = vk;
				// Key down the actual key-code
				SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				// Key up the actual key-code
				structInput.inputUnion.ki.dwFlags = KEYEVENTF_KEYUP;
				SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				if (shift)
				{
					structInput.inputUnion.ki.wVk = 0x10;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				if (ctrl)
				{
					structInput.inputUnion.ki.wVk = 0x11;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				if (alt)
				{
					structInput.inputUnion.ki.wVk = 0x12;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
				if (win)
				{
					structInput.inputUnion.ki.wVk = 0x5b;
					SendInput(1, ref structInput, Marshal.SizeOf(structInput));
				}
			}
			catch (Exception ex)
			{
				cr.Type = CommandResultType.Exception;
				cr.Content = ex.ToString();
			}

			return cr;
		}

	}
}
